package com.contest.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.contest.constant.CommonsConstant;
import com.contest.dao.GameNumbersRuleDao;
import com.contest.entity.*;
import com.contest.listener.SimpleApplicationListener;
import com.contest.quartz.QuartzManager;
import com.contest.quartz.data.ScheduleJob;
import com.contest.quartz.task.TaskGameNumbersRun;
import com.contest.quartz.task.TaskTest;
import com.contest.service.*;
import com.contest.util.*;
import com.contest.ws.WsGameNumber;
import org.quartz.SchedulerException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.socket.TextMessage;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * <p>
 * ${table.comment}  服务实现类
 * </p>
 *
 * @author yfl
 * @since ${date}
 */
@Service
@Transactional
public class GameNumbersRuleServiceImpl extends  ServiceImpl<GameNumbersRuleDao, GameNumbersRule> implements GameNumbersRuleService {

    @Autowired
    IdWorker idWorker;

    @Autowired
    GameNumbersResultService gameNumbersResultService;


    @Autowired
    UserInfoService userInfoService;
    @Autowired
    GameNumbersTransactionRecordService gameNumbersTransactionRecordService;
    @Autowired
    GameNumbersBettingTypeService gameNumbersBettingTypeService;
    @Autowired
    GamingOddsService gamingOddsService;

    Logger logger = LoggerFactory.getLogger(GameNumbersRuleServiceImpl.class);



    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public R save(GameNumbersRule gameNumbersRule) {

        EntityWrapper<GameNumbersRule> ewRule = new EntityWrapper<>();
        ewRule.eq("fd_date" , gameNumbersRule.getFdDate());
        GameNumbersRule rule =  this.selectOne(ewRule);
        if(rule != null)
            return R.error("期号日期已存在");

        Integer min = gameNumbersRule.getFdNumberMin();
        Integer max = gameNumbersRule.getFdNumberMax();
        if(min >= max)
            return  R.error("数据区间非法");

        String produceTime = gameNumbersRule.getFdProduceStartTime();
        String nowTime = DateUtil.yyyyMMddHHmm(new Date());
        if(nowTime.compareTo(produceTime) >= 0)
            return  R.error("当前时间大于数据数据产生时间");

        gameNumbersRule.setFdId(idWorker.nextId() + "");
        gameNumbersRule.setFdCreateTime(DateUtil.now());
        if (this.insert(gameNumbersRule)) {

            insertResult(gameNumbersRule, min, max);

            try {
                createJob(gameNumbersRule);
            } catch (Exception e) {
                e.printStackTrace();    throw new RuntimeException("crate job error");//为了事务回滚
            }

            return R.ok("操作成功!");

        } else {

            return R.error("操作失败");

        }

    }

    /**
     * web启动时应用
     * 初始化当天的定时任务
     */
    @Override
    public void initCurrentDateGameRule()
    {
        EntityWrapper<GameNumbersRule> ewRule = new EntityWrapper<>();
        ewRule.eq("fd_date" , DateUtil.yyyyMMddNow());
        GameNumbersRule gameNumbersRule =  this.selectOne(ewRule);
        if(gameNumbersRule != null)
        {

            EntityWrapper<GameNumbersResult> ew = new EntityWrapper<>();
            ew.eq("fd_rule_id" , gameNumbersRule.getFdId());
            List<GameNumbersResult> list = gameNumbersResultService.selectList(ew);
            if(list == null || list.size() == 0)
            {
                insertResult(gameNumbersRule, gameNumbersRule.getFdNumberMin()  , gameNumbersRule.getFdNumberMax());
            }
            try {
                createJob(gameNumbersRule);
            } catch (Exception e) {
                e.printStackTrace();    throw new RuntimeException("crate job error");//为了事务回滚
            }
        }
    }

    @Override
    public void getCurrentDateGameNumberInfos(HttpServletRequest request, Map msg) {

        //rule info
        EntityWrapper<GameNumbersRule> ewRule = new EntityWrapper<>();
        ewRule.eq("fd_date" , DateUtil.yyyyMMddNow());
        GameNumbersRule rule =  this.selectOne(ewRule);
        if(rule == null)
        {
            msg.put("code" , 0);
            msg.put("msg" , "没有当天的游戏");
            return;
        }

        //往期数据
        Integer preoidIndex = rule.getFdPeriods();
        String nowDate = DateUtil.yyyyMMddHHmm(new Date());
        String sProduceEndTime = rule.getFdProduceEndTime();

        EntityWrapper<GameNumbersResult> ewResut = new EntityWrapper<>();
        ewResut.eq("fd_rule_id" , rule.getFdId());
//        ewResut.le("fd_preiod_index" , preoidIndex);
        ewResut.le("fd_produce_date" , nowDate);
        List<GameNumbersResult> listResult = gameNumbersResultService.selectList(ewResut);

        //user info
        UserInfo userInfo = UserLoginUtil.getCurrentUserInfo(request);
        msg.put("code" , 1);
        msg.put("rulues" , rule);
        msg.put("result" , listResult);
        msg.put("userId" , userInfo.getFdId());

    }

    @Override
    public R updateRule(GameNumbersRule gameNumbersRule) {

        EntityWrapper<GameNumbersRule> ewRule = new EntityWrapper<>();
        ewRule.eq("fd_date" , gameNumbersRule.getFdDate());
        GameNumbersRule rule =  this.selectOne(ewRule);
        if(rule != null && !gameNumbersRule.getFdId().equals(rule.getFdId()))
            return R.error("期号日期已存在");

        String nowDate = DateUtil.yyyyMMddHHmm(new Date());
        String beginTime = rule.getFdStartTime();
        if(nowDate.compareTo(beginTime) > 0)
        {
            return R.error("超过开盘时间,已不能更改");
        }

        Integer min = gameNumbersRule.getFdNumberMin();
        Integer max = gameNumbersRule.getFdNumberMax();
        if(min >= max)
            return  R.error("数据区间非法");


        if (this.updateById(gameNumbersRule))
        {
            deleteOldData(gameNumbersRule);
            insertResult(gameNumbersRule, min, max);
            try {
                createJob(gameNumbersRule);
            } catch (Exception e) {
                e.printStackTrace();    throw new RuntimeException("crate job error");//为了事务回滚
            }

            return R.ok("更新成功!");
        } else {
            return R.error(-1, "更新失败!");
        }
    }

    @Override
    public R deleteRule(String[] ids) {

        EntityWrapper<GameNumbersRule> ewRule = new EntityWrapper<>();
        ewRule.eq("fd_date" , DateUtil.yyyyMMddNow());
        GameNumbersRule rule =  this.selectOne(ewRule);
        String ruleId = rule != null ? rule.getFdId() : null;

        List<String> fdIds = new ArrayList<String>();
        for (String id : ids) {
            fdIds.add(id);
        }
        if(fdIds.contains(ruleId))
        {
            EntityWrapper<GameNumbersResult> ew = new EntityWrapper<>();
            ew.eq("fd_rule_id" , ruleId);
            gameNumbersResultService.delete(ew);

            try {
                stopJob(rule);
            } catch (SchedulerException e) {
                e.printStackTrace();
            }
        }

        if (this.deleteBatchIds(fdIds)) {
            return R.ok("删除成功");
        } else {
            return R.error(-1, "删除失败!");
        }
    }


    private void insertResult(GameNumbersRule gameNumbersRule, Integer min, Integer max) {
        Integer periods = gameNumbersRule.getFdPeriods();
        String sProduceStartTime = gameNumbersRule.getFdProduceStartTime();
        Integer intervalTime = gameNumbersRule.getFdIntervalTime();
        Date produceStartTime = DateUtil.yyyyMMddHHmm(sProduceStartTime);
        for(int i = 0 ; i < periods ; i++)
        {
            Integer result = RandomUtil.randomNumber(min , max);
            Date date = DateUtil.addMinutes(produceStartTime , intervalTime * i);
            String periodProduceTime = DateUtil.yyyyMMddHHmm(date);

            GameNumbersResult gameNumbersResult = new GameNumbersResult();
            gameNumbersResult.setFdId(idWorker.nextId() + "");
            gameNumbersResult.setFdRuleId(gameNumbersRule.getFdId());
            gameNumbersResult.setFdDate(gameNumbersRule.getFdDate());
            gameNumbersResult.setFdPeriodResult(result);
            gameNumbersResult.setFdPreiodIndex(i + 1);
            gameNumbersResult.setFdProduceDate(periodProduceTime);
            gameNumbersResultService.insert(gameNumbersResult);
        }
    }


    private void deleteOldData(GameNumbersRule gameNumbersRule)
    {
        EntityWrapper<GameNumbersResult> ew = new EntityWrapper<>();
        ew.eq("fd_rule_id" , gameNumbersRule.getFdId());
        gameNumbersResultService.delete(ew);
    }


    private void stopJob(GameNumbersRule rule) throws SchedulerException {

        ScheduleJob job = getScheduleJob(rule  , rule.getFdProduceStartTime());
        QuartzManager.getInstance().deleteJob(job);

    }


    private void createJob(GameNumbersRule gameNumbersRule) throws Exception {

        String fdDate = gameNumbersRule.getFdDate();
        String nowDate = DateUtil.yyyyMMddNow();
        if(!nowDate.equals(fdDate))
            return;

        String startTime = gameNumbersRule.getFdProduceStartTime();

        String nowTime = DateUtil.yyyyMMddHHmm(new Date());
        if(nowTime.compareTo(startTime) >= 0 )
        {
            Date startTimeD = DateUtil.yyyyMMddHHmm(startTime);
            Date nowTimeD = DateUtil.yyyyMMddHHmm(nowTime);
            long startTimeL = startTimeD.getTime();
            long nowTimeL = nowTimeD.getTime();
            Double startTimeDou= new Double(startTimeL);
            Double nowTimeDou = new Double(nowTimeL);

            int interval = gameNumbersRule.getFdIntervalTime();
            Double r = (nowTimeDou - startTimeDou)/(interval*60*1000);
            System.out.println((int)Math.floor(r));
            Integer index = (int)Math.floor(r);
            //上一次的执行时间
            Date perioddTimeD = DateUtil.addMinutes(startTimeD , index * interval);
            String periodTime = DateUtil.yyyyMMddHHmm(perioddTimeD);

            Date nextTime = DateUtil.addMinutes(perioddTimeD , gameNumbersRule.getFdIntervalTime());
            String nextHH = DateUtil.HHmm(nextTime).split(":")[0];
            String curHH = DateUtil.HHmm(perioddTimeD).split(":")[0];
            if(!curHH.equals(nextHH))
                startTime = DateUtil.yyyyMMddHHmm(nextTime);
            else
                startTime = periodTime;
        }
        ScheduleJob job = getScheduleJob(gameNumbersRule , startTime);
        QuartzManager.getInstance().addJob(job);
        logger.debug("add a job:" + JSON.toJSONString(job));
    }

    public ScheduleJob getScheduleJob(GameNumbersRule gameNumbersRule , String startTime)
    {
        String fdDate = gameNumbersRule.getFdDate();
//        String sProduceStartTime = gameNumbersRule.getFdProduceStartTime();
        String sProduceEndTime = gameNumbersRule.getFdProduceEndTime();
        Integer intervalTime = gameNumbersRule.getFdIntervalTime();

//        String shhMM = sProduceStartTime.split(" ")[1];
        String shhMM = startTime.split(" ")[1];
        String[] hhmmArr = shhMM.split(":");
        String shh = hhmmArr[0];
        String smm = hhmmArr[1];
        String ehhMM = sProduceEndTime.split(" ")[1];
        String ehh = ehhMM.split(":")[0];
        String expressionFormat = "0 %s %s * * ? *";

        String objMM = smm + "/" + intervalTime;
//        String objMM = "*/" + intervalTime;
        String objHH = shh + "-" + ehh;

        ScheduleJob job = new ScheduleJob();
        job.setJobName("game" + fdDate);
        job.setJobGroup("numberGame");
        job.setJobStatus(ScheduleJob.STATUS_RUNNING);
        //"0 15/10 9-18 19 * ? *"
        job.setCronExpression(String.format(expressionFormat , objMM , objHH));
        job.setDescription("game" + fdDate);
        job.setBeanClass(TaskGameNumbersRun.class.getName());
        job.setIsConcurrent(ScheduleJob.CONCURRENT_NOT);
        job.setMethodName("run");
        job.setSpringId("taskGameNumbersRun");
        job.getDataMap().put("gameNumbersRule" , gameNumbersRule);
        job.getDataMap().put("preoidIndex"	 , 0);
        return job;
    }

    @Override
    public WsGameNumber buyNumber(WsGameNumber wsGameNumber) {
        float amount = wsGameNumber.getFdMoney().floatValue();
        UserInfo userInfo = userInfoService.selectById(wsGameNumber.getUserId());
        float over = userInfo.getFdOver().floatValue();
        WsGameNumber result = new WsGameNumber();
        result.setReturnType(1);
        result.setFdPreiodIndex(wsGameNumber.getFdPreiodIndex());

        if (over > amount)
        {
            GamingOdds gamingOdds = gamingOddsService.selectById(wsGameNumber.getFdBettingTypeId());

            userInfo.setFdOver(BigDecimal.valueOf(over - amount));
            userInfoService.updateById(userInfo);

            EntityWrapper<GameNumbersResult> ewResult = new EntityWrapper<GameNumbersResult>();
            ewResult.eq("fd_preiod_index" , wsGameNumber.getFdPreiodIndex());
            ewResult.eq("fd_date" , DateUtil.yyyyMMddNow());
            GameNumbersResult gameNumbersResult = gameNumbersResultService.selectOne(ewResult);
            if(gameNumbersResult == null)
            {
                result.setContent("购买失败");
                return result;
            }

            String sProduceDate = gameNumbersResult.getFdProduceDate();
            String sNowDate = DateUtil.yyyyMMddHHmm(new Date());
            if( sNowDate.compareTo(sProduceDate) >= 0)
            {
                result.setContent("此期已出，不能购买");
                return result;
            }

            Integer fdNumber = wsGameNumber.getFdNumber();
            //0大小,1单双,2特码
            Integer gamingFdType = gamingOdds.getFdType();
            String fdTypeName = "";
            if(0 == gamingFdType){
                if(fdNumber == 43210 )
                    fdTypeName = "买小";
                else if(fdNumber == 98765)
                    fdTypeName = "买大";
                else {
                    result.setContent("购买失败");
                    return result;
                }
            }

            if(1 == gamingFdType){
                if(fdNumber == 97531 )
                    fdTypeName = "买单";
                else if(fdNumber == 86420)
                    fdTypeName = "买双";
                else {
                    result.setContent("购买失败");
                    return result;
                }
            }

            if(2 == gamingFdType){
                if(fdNumber >= 0 && fdNumber <= 9 )
                    fdTypeName = "特码";
                else {
                    result.setContent("购买失败");
                    return result;
                }
            }

            GameNumbersTransactionRecord record = new GameNumbersTransactionRecord();
            record.setFdId(idWorker.nextId() + "");
            record.setFdResultId(gameNumbersResult.getFdId());
            record.setFdDate(DateUtil.yyyyMMddNow());
            record.setFdPreiodIndex(wsGameNumber.getFdPreiodIndex());
            record.setFdMoney(wsGameNumber.getFdMoney());
            record.setFdCreateTime(DateUtil.yyyyMMddHHmm(new Date()));
            record.setFdNumber(fdNumber);
            record.setFdBettingTypeId(wsGameNumber.getFdBettingTypeId());
            record.setFdBettingTypeName(fdTypeName);
            record.setFdBettingTypeScale(gamingOdds.getFdOdds());
            record.setFdUserId(userInfo.getFdId());
            record.setFdUserName(userInfo.getFdNickname());
            record.setFdIsWinning(0);
            record.setFdIsSettle(0);
            gameNumbersTransactionRecordService.insert(record);
            result.setContent("购买成功&nbsp;<br>"+"金币：<span style='color:red'>"+amount+"<span>");
        }else
        {

            result.setContent("账户金额不足!");
        }
        return result;
    }
}
